#include <d3d11.h>
#include <d3dcompiler.h>
#include <d3dx11.h>
#include <msclr/marshal.h>

#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
#endif

using namespace System;
using namespace System::IO;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;
using namespace msclr::interop; 

namespace ManagedShader {
  public ref class ShaderCompiler	{
  public:
    ShaderCompiler(){}

    array<unsigned char>^ CompileFromFile(String^ Path, String^ EntryFunction, String^ ShaderProfile){
      marshal_context^ context = gcnew marshal_context();

      LPCWSTR _path = context->marshal_as<LPCWSTR>(Path);
      LPCSTR _entryFunction = context->marshal_as<LPCSTR>(EntryFunction);
      LPCSTR _shaderProfile = context->marshal_as<LPCSTR>(ShaderProfile);
      ID3DBlob* pErrorBlob = NULL;
      ID3DBlob* pBlob = NULL;

      try{
        HRESULT compileResult = D3DX11CompileFromFile( 
          _path, //pSrcFile
          NULL, //pDefines
          NULL, //pInclude
          _entryFunction, //pFunctionName
          _shaderProfile, //pProfile
          NULL, //Shader compile flags.
          NULL, //Effect compile flags.
          NULL, //A pointer to a thread pump interface (see ID3DX11ThreadPump Interface). 
          &pBlob, //A pointer to memory which contains the compiled shader
          &pErrorBlob, //A pointer to memory which contains a listing of errors and warnings 
          NULL //A pointer to the return value
          );

        if (!SUCCEEDED(compileResult)) {
          char *nativeErrorString = (char *)(pErrorBlob->GetBufferPointer());
          String^ managedErrorString = context->marshal_as<String^>(nativeErrorString);
          throw gcnew System::ArgumentException(managedErrorString);
        }
        else {
          int size = pBlob->GetBufferSize();
          array<unsigned char>^ res = gcnew array<unsigned char>(size);
          Marshal::Copy(res, 0, IntPtr(pBlob->GetBufferPointer()), size);
          return res;
        }
      }
      finally{
        SAFE_RELEASE( pErrorBlob );
        SAFE_RELEASE( pBlob );
      }
    }

    void CompileFromHlsl(String^ hlsl){
      
    }
  };
}
